import Joi from 'joi';
import Boom from '@hapi/boom';
import set from 'lodash/set';
import { pathOr } from 'ramda';
import { format as csvFormat } from '@fast-csv/format';
import fetch from 'fermionjs/lib/fetch';
import { NEW_USER } from '@kuiper/auth';

import debugCheck from '@kuiper-helpers/debug-check';
import { casRespParser } from '@kuiper/auth/dist/server/routes/casLogin';
import pkg from '../../../package.json';
import urlPathConcat from '@kuiper-helpers/url-path-concat/dist';

import joiOmit from '../helpers/joiOmit';
import joiToFilterArg from '../helpers/joiToFilterArg';
import errorCodesBuilder from '../helpers/errorCodesBuilder';
import extractQueryFieldByJoi from '../helpers/extractQueryFieldByJoi';
import { datetime } from '../../common/formatter';
const { name } = pkg;

const errorCodes = errorCodesBuilder();

// #region user
export const user = Joi.object({
	_id: Joi.any().meta({ _mongoose: { type: 'ObjectId' } }),
	type: Joi.string().description('类型'),
	username: Joi.string().description('用户名'),
	phone: Joi.string().allow(null, '').description('电话'),
	phoneVerified: Joi.boolean().description('是否验证电话'),
	email: Joi.string().allow(null, '').description('邮箱'),
	emailVerified: Joi.boolean().description('是否验证邮箱'),
	wechat: Joi.object({
		wx_openid: Joi.string().allow(null, '').description('微信ID'),
		nickname: Joi.string().allow(null, '').description('昵称'),
	})
		.allow(null)
		.description('微信'),
	idCardNum: Joi.string().allow(null, '').description('身份证号'),
	identified: Joi.boolean().description('是否实名认证'),
	source: Joi.string().allow(null, '').description('注册来源'),
	roleIds: Joi.array().items(Joi.string()).description('角色ID'),
	createAt: Joi.date().description('创建时间'),
	lastActiveTime: Joi.date().allow(null).description('最近活跃时间'),
	planId: Joi.string().allow(null, '').description('套餐ID'),
	enableSingleDevice: Joi.boolean()
		.allow(null)
		.default(false)
		.description('单设备登录保护'),
	enableMfa: Joi.boolean().allow(null).default(false).description('登录保护'),
	realname: Joi.string().allow(null, ''),
	province: Joi.string().allow(null, ''),
	attribution: Joi.string().allow(null, ''),
	createBy: Joi.string().allow(null, ''),
	expireAt: Joi.string().allow(null, ''),
	note: Joi.string().allow(null, ''),
	address: Joi.string().allow('', null),
	qq: Joi.string().allow(null, ''),
	registerTime: Joi.date().allow(null),
	accessLimit: Joi.number(),
	customerServiceRep: Joi.string().allow(null, '').description('跟进客服'),
}).label('User');
// #endregion

const findManyQueryValidate = joiToFilterArg(user)
	.keys({
		usernameSearch: Joi.string(),
	})
	.unknown();
export const findMany = {
	tags: ['api', 'user'],
	description: '用户列表',
	auth: debugCheck('disable-open-api-auth')
		? false
		: {
				mode: 'required',
				access: {
					// 只允许使用 URL Token 访问
					entity: 'app',
				},
		  },

	validate: {
		query: findManyQueryValidate,
	},

	response: {
		schema: Joi.object({
			data: Joi.object({
				UserMany: Joi.array().items(
					user.append({
						usernameSearch: Joi.string().allow(null, ''),
						plan: Joi.object({
							_id: Joi.string().description('套餐ID'),
							name: Joi.string().description('套餐名'),
						})
							.allow(null)
							.default({})
							.description('用户购买的套餐'),
					})
				),
			}),
		}).label('Response'),
	},

	handler: {
		injectGraphql: {
			payloadBuilder(request) {
				const {
					query,
					server: {
						app: {
							config: {
								'@kuiper/auth': { authBackend },
							},
						},
					},
				} = request;

				request.log.debug({ query }, '请求用户列表');
				if (authBackend === 'cas') {
					query.type = 'cas';
				}

				return JSON.stringify({
					variables: {
						filter: extractQueryFieldByJoi({
							query,
							schema: findManyQueryValidate,
						}),
					},
					// #region query
					query: `
						query($filter: FilterFindManyUserInput) {
							UserMany(filter: $filter, sort: _ID_DESC, limit: 0) {
								_id
								username
								phone
								phoneVerified
								email
								emailVerified
								wechat {
									wx_openid
									nickname
								}
								idCardNum
								identified
								source
								roleIds
								createAt
								lastActiveTime
								realname
								province
								attribution
								createBy
								expireAt
								note
								address
								qq
								enableSingleDevice
								enableMfa
								registerTime
								accessLimit
								customerServiceRep
								plan {
									_id
									name
								}
							}
						}`,
					// #endregion
				});
			},
		},
	},
};

export const findById = {
	tags: ['api', 'user'],
	description: '用户按ID查看',
	auth: debugCheck('disable-open-api-auth')
		? false
		: {
				mode: 'required',
				access: {
					// 只允许使用 URL Token 访问
					entity: 'app',
				},
		  },

	validate: {
		params: Joi.object({
			userId: Joi.string().description('用户ID'),
		}),
	},

	response: {
		schema: Joi.object({
			data: Joi.object({
				UserById: user.append({
					plan: Joi.object({
						_id: Joi.string().description('套餐ID'),
						name: Joi.string().description('套餐名'),
					})
						.allow(null)
						.default({})
						.description('用户购买的套餐'),
				}),
			}),
		}).label('Response'),
	},

	handler: {
		injectGraphql: {
			payloadBuilder(request) {
				const {
					params: { userId },
				} = request;

				return JSON.stringify({
					variables: { _id: userId },
					// #region query
					query: `
						query($_id: MongoID!) {
							UserById: UserOne(filter: { _id: $_id }) {
								_id
								username
								phone
								phoneVerified
								email
								emailVerified
								wechat {
									wx_openid
									nickname
								}
								idCardNum
								identified
								source
								roleIds
								createAt
								lastActiveTime
								planId
								realname
								province
								attribution
								createBy
								expireAt
								note
								address
								qq
								enableSingleDevice
								enableMfa
								registerTime
								accessLimit
								customerServiceRep
								plan {
									_id
									name
								}
							}
						}`,
					// #endregion
				});
			},
		},
	},
};

export const pagination = {
	tags: ['api', 'user'],
	description: '用户列表-分页查看',
	auth: debugCheck('disable-open-api-auth')
		? false
		: {
				mode: 'required',
				access: {
					// 只允许使用 URL Token 访问
					entity: 'app',
				},
		  },

	validate: {
		payload: Joi.object({
			page: Joi.number().default(1).description('当前页'),
			perPage: Joi.number().default(10).description('每页条目数'),
			filter: joiToFilterArg(user)
				.append({
					createAtGTE: Joi.date().description('创建时间大于等于'),
					createAtLTE: Joi.date().description('创建时间小于等于'),
					lastActiveTimeGTE: Joi.date().description('最近活跃时间大于等于'),
					lastActiveTimeLTE: Joi.date().description('最近活跃时间小于等于'),
					usernameSearch: Joi.string(),
					planExpireTime: Joi.any(),
				})
				.description('过滤条件'),
		}),
	},

	response: {
		schema: Joi.object({
			data: Joi.object({
				UserPagination: Joi.object({
					count: Joi.number().description('总条数'),
					pageInfo: Joi.object({
						currentPage: Joi.number().description('当前页数'),
						perPage: Joi.number().description('每页条数'),
						pageCount: Joi.number().description('总页数'),
						itemCount: Joi.number().description('总条数'),
						hasNextPage: Joi.boolean().description('是否有下一页'),
						hasPreviousPage: Joi.boolean().description('是否有上一页'),
					}),
					items: Joi.array().items(
						user.append({
							plan: Joi.object({
								_id: Joi.string().description('套餐ID'),
								name: Joi.string().description('套餐名'),
								marks: Joi.string().description('套餐别名'),
								planExpireTime: Joi.date().allow(null),
							})
								.allow(null)
								.default({})
								.description('用户购买的套餐'),
						})
					),
				}),
			}),
		}).label('Response'),
	},

	handler: {
		injectGraphql: {
			payloadBuilder(request) {
				const {
					payload: { page, perPage, filter },
					server: {
						app: {
							config: {
								'@kuiper/auth': { authBackend },
							},
						},
					},
				} = request;
				// FIXME: 后端只能传字符类型的 'null'
				if (filter.planId === 'null') {
					filter.planId = null;
				}
				if (authBackend === 'cas') {
					filter.type = 'cas';
				}
				return JSON.stringify({
					variables: {
						page,
						perPage,
						filter,
					},
					// #region query
					query: `
					query($page: Int = 1, $perPage: Int = 10, $filter: FilterFindManyUserInput, $sort: SortFindManyUserInput = _ID_DESC) {
							UserPagination(page: $page, perPage: $perPage, filter: $filter, sort: $sort) {
								count
								pageInfo {
									currentPage
									perPage
									pageCount
									itemCount
									hasNextPage
									hasPreviousPage
								}
								items {
									_id
									username
									phone
									phoneVerified
									email
									emailVerified
									wechat {
										wx_openid
										nickname
									}
									idCardNum
									identified
									source
									roleIds
									createAt
									lastActiveTime
									planId
									realname
									province
									attribution
									createBy
									expireAt
									note
									address
									qq
									enableSingleDevice
									enableMfa
									registerTime
									accessLimit
									customerServiceRep
									plan {
										_id
										name
                    marks
										planExpireTime
									}
								}
							}
						}`,
					// #endregion
				});
			},
		},
	},
};

export const count = {
	tags: ['api', 'user'],
	description: '用户数量统计',
	auth: debugCheck('disable-open-api-auth')
		? false
		: {
				mode: 'required',
				access: {
					// 只允许使用 URL Token 访问
					entity: 'app',
				},
		  },

	validate: {
		payload: Joi.object({
			filter: user.description('过滤条件'),
		}),
	},

	response: {
		schema: Joi.object({
			data: Joi.object({
				UserCount: Joi.number().description('数量'),
			}),
		}).label('Response'),
	},

	handler: {
		injectGraphql: {
			payloadBuilder(request) {
				const {
					payload: { filter },
				} = request;

				return JSON.stringify({
					variables: { filter },
					// #region query
					query: `
						query($filter: FilterCountUserInput) {
							UserCount(filter: $filter)
						}`,
					// #endregion
				});
			},
		},
	},
};

export const create = {
	tags: ['api', 'user'],
	description: '创建用户',
	auth: debugCheck('disable-open-api-auth')
		? false
		: {
				mode: 'required',
				access: {
					// 只允许使用 URL Token 访问
					entity: 'app',
				},
		  },

	validate: {
		payload: Joi.object({
			username: Joi.string().description('用户名'),
			type: Joi.string().valid('email', 'phone').description('账号类型'),
			planId: Joi.string().allow('', null).description('套餐ID'),
		}),
	},

	response: {
		schema: Joi.object({
			_id: Joi.any().description('用户ID'),
			username: Joi.string().description('用户名'),
			password: Joi.string().allow('').description('密码'),
		}),
	},

	async handler(request, h) {
		const {
			payload: { username, planId, type },
			server: {
				events,
				methods: { initBalance },
				app: {
					model: { Role, User },
					config: {
						[name]: { casAppId, serviceApi, token },
					},
				},
			},
		} = request;

		try {
			const user = await User.findOne({ username });
			if (user) {
				request.log.error({ username }, '[新建用户]用户已创建');
				return { _id: user._id, username: user.username, password: '' };
			}
		} catch (err) {
			const error = errorCodes['[13][新建用户]查询Mongo失败'].from(err);
			request.log.error({ error, username, planId }, error.rawMessage);
			throw error;
		}

		let casProfile;
		try {
			const resp = await fetch(urlPathConcat(serviceApi, '/api/v2/users/cas'), {
				method: 'POST',
				body: JSON.stringify({
					url: `/accounts/${casAppId}/verify/`,
					body: { username },
				}),
				headers: {
					cookie: `token=${token}`,
					'content-type': 'application/json',
				},
			}).then(response => {
				if (!response || response.status !== 'success') {
					request.log.error({ error: response.message }, response.message);
					throw Boom.badRequest(response.message);
				}
				return response.data;
			});
			if (resp.result) {
				request.log.debug({ username }, 'CAS用户已创建');
				casProfile = {
					username,
					[type]: username,
				};
			}
		} catch (err) {
			const error = errorCodes['[12][新建用户]查询CAS用户失败'].from(err);
			request.log.error({ error, username, planId }, error.rawMessage);
			return h.response(error.rawMessage).code(503);
		}

		let password = '';
		if (!casProfile) {
			password = (Math.random() + Date.now()).toString(32);
			const payload = {
				password,
				register_method: type,
				email: username,
			};
			try {
				const resp = await fetch(
					urlPathConcat(serviceApi, '/api/v2/users/cas'),
					{
						method: 'POST',
						body: JSON.stringify({
							url: `/accounts/${casAppId}/batch_create/`,
							body: payload,
						}),
						headers: {
							cookie: `token=${token}`,
							'content-type': 'application/json',
						},
					}
				).then(response => {
					if (!response || response.status !== 'success') {
						request.log.error({ error: response.message }, response.message);
						throw Boom.badRequest(response.message);
					}
					return response.data;
				});
				// eslint-disable-next-line camelcase
				if (!resp?.message?.register_num) {
					password = '';
				}
				request.log.info({ username, resp }, '创建CAS用户成功');
			} catch (err) {
				const error = errorCodes['[12][新建用户]创建CAS用户失败'].from(err);
				request.log.error(
					{ error, username, planId, payload },
					error.rawMessage
				);
				throw error;
			}
			casProfile = {
				username,
				[type]: username,
			};
		}

		let user;
		try {
			const role = await Role.findOne({
				isDefaultRole: true,
			});
			user = new User({
				...casRespParser(casProfile),
				username,
				type: 'cas',
				roleIds: [role.id],
				[type]: username,
			});
			await user.save();
			events.emit(NEW_USER, user);

			if (planId) {
				await initBalance({ planId, uid: user._id });
			}
		} catch (err) {
			const error = errorCodes[
				'[12][新建用户]初始化余额信息Mongo记录失败'
			].from(err);
			request.log.error({ error, username, planId }, error.rawMessage);
			throw error;
		}

		return { password, _id: user._id, username: user.username };
	},
};

const userCsvHeader = [
	{ name: '用户名', fieldPath: ['username'] },
	{ name: '姓名', fieldPath: ['realname'] },
	{ name: '邮箱', fieldPath: ['email'] },
	{ name: '归属地', fieldPath: ['attribution'] },
	{ name: '套餐', fieldPath: ['planName'] },
	{ name: '注册来源', fieldPath: ['source'] },
	{ name: '注册时间', fieldPath: ['createAt'], render: datetime },
	{ name: '最近登录时间', fieldPath: ['lastActiveTime'], render: datetime },
	{ name: '跟进客服', fieldPath: ['customerServiceRep'] },
];
export const download = {
	tags: ['api', 'user'],
	description: '下载用户列表',
	auth: debugCheck('disable-open-api-auth')
		? false
		: {
				mode: 'required',
				access: {
					// 只允许使用 URL Token 访问
					entity: 'app',
				},
		  },

	timeout: { socket: false },

	validate: {
		query: joiOmit(joiToFilterArg(user))
			.append({
				createAtGTE: Joi.date().description('创建时间大于等于'),
				createAtLTE: Joi.date().description('创建时间小于等于'),
				lastActiveTimeGTE: Joi.date().description('最近活跃时间大于等于'),
				lastActiveTimeLTE: Joi.date().description('最近活跃时间小于等于'),
			})
			.unknown()
			.description('过滤条件'),
	},

	handler(request, h) {
		const {
			query: queryParams,
			server: {
				app: {
					model: { User, Package },
				},
			},
			auth: {
				credentials: { uid, permissionMap },
			},
		} = request;

		const stream = csvFormat({
			writeBOM: true,
			headers: userCsvHeader.map(x => x.name),
		}).transform(row => {
			return userCsvHeader.reduce((obj, { name, fieldPath, render }) => {
				obj[name] = pathOr('', fieldPath, row);
				if (render) obj[name] = render(obj[name]);
				return obj;
			}, {});
		});

		let query = queryParams;
		if (query.createAtGTE) {
			set(query, ['createAt', '$gte'], query.createAtGTE);
			delete query.createAtGTE;
		}
		if (query.createAtLTE) {
			set(query, ['createAt', '$lte'], query.createAtLTE);
			delete query.createAtLTE;
		}
		if (query.lastActiveTimeGTE) {
			set(query, ['lastActiveTime', '$gte'], query.lastActiveTimeGTE);
			delete query.lastActiveTimeGTE;
		}
		if (query.lastActiveTimeLTE) {
			set(query, ['lastActiveTime', '$lte'], query.lastActiveTimeGTE);
			delete query.lastActiveTimeLTE;
		}

		if (!permissionMap['auth\x01listUser\x010']) {
			set(query, ['uid'], uid);
		}
		query = extractQueryFieldByJoi({
			query: queryParams,
			schema: user,
		});

		setTimeout(async () => {
			try {
				for await (const record of User.find(query).sort({ _id: -1 })) {
					const record_ = record.toObject();
					const rowP = await Package.find({ _id: record_.planId });
					const rowP_ = rowP[0] ? rowP[0].toObject() : {};
					record_.planName = rowP_.name;
					stream.write(record_);
				}
				stream.end();
			} catch (err) {
				stream.end();
				const error = errorCodes['[12]用户日志下载失败'].from(err);
				request.log.error({ error }, error.rawMessage);
			}
		});

		return h
			.response(stream)
			.header('Content-Type', 'text/csv')
			.header('Content-Disposition', 'attachment; filename="user.csv"');
	},
};

export const unbindMfa = {
	tags: ['api', 'alert', 'wechat'],
	description: '取消登录保护',
	auth: debugCheck('disable-open-api-auth')
		? false
		: {
				mode: 'required',
				access: {
					// 只允许使用 URL Token 访问
					entity: 'app',
				},
		  },

	validate: {
		params: Joi.object({
			userId: Joi.string().description('用户UID'),
		}),
	},

	response: {
		schema: Joi.object({
			message: Joi.string(),
		}).label('Response'),
	},

	async handler(request) {
		const {
			params: { userId },
			server: {
				app: {
					model: { User },
					config: {
						[name]: { casAppId, serviceApi, token },
					},
				},
			},
		} = request;

		let user;
		try {
			user = await User.findOne({ _id: userId });
			if (!user) {
				request.log.warn({ userId }, '取消登录保护:没有找到对应用户');
				throw new Error('Invalid userId');
			}
		} catch (err) {
			const error = errorCodes['[0A][取消登录保护]查询对应用户信息失败'].from(
				err
			);
			request.log.error({ error }, error.rawMessage);
			throw Boom.boomify(error);
		}

		try {
			const response = await fetch(
				urlPathConcat(serviceApi, '/api/v2/users/cas'),
				{
					method: 'POST',
					body: JSON.stringify({
						url: `/accounts/${casAppId}/mfa_unbind/`,
						body: {
							username: user.username,
						},
					}),
					headers: {
						cookie: `token=${token}`,
						'content-type': 'application/json',
					},
				}
			).then(resp => {
				if (!resp || resp.status !== 'success') {
					request.log.error({ error: resp.message }, resp.message);
					throw Boom.badRequest(resp.message);
				}
				return resp.data;
			});
			request.log.audit(
				{
					actionType: 'delete',
					module: 'user',
					response,
					actionMessage: '取消MFA',
					detail: `${user.username}取消MFA`,
				},
				`[${user.username}]取消MFA`
			);
		} catch (err) {
			const error = errorCodes['[0A][取消登录保护]请求CAS API失败'].from(err);
			request.log.error({ error }, error.rawMessage);
			throw Boom.badRequest(error.detail);
		}

		return {
			message: '取消成功',
		};
	},
};
